package com.nuo.backend.modules.security.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.nuo.backend.common.utils.*;
import com.nuo.backend.modules.security.entity.*;
import com.nuo.backend.modules.security.po.JwtPO;
import com.nuo.backend.modules.security.po.RegisterPO;
import com.nuo.backend.modules.security.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.nuo.backend.modules.security.dao.SecUserDao;

import javax.servlet.http.HttpServletRequest;


@Slf4j
@Service("secUserService")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SecUserServiceImpl extends ServiceImpl<SecUserDao, SecUserEntity> implements SecUserService {

    final JwtUtils jwtUtils;
    final SecUserTokenService secUserTokenService;
    final SecRoleService secRoleService;
    final SecUserRoleService secUserRoleService;
    final SecMenuService secMenuService;
    final SecRoleMenuService secRoleMenuService;

    final RedisUtils redisUtils;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SecUserEntity> page = this.page(
                new Query<SecUserEntity>().getPage(params),
                new LambdaQueryWrapper<SecUserEntity>()
                        .eq(ObjectUtil.isNotEmpty(params.get("createUserId")), SecUserEntity::getCreateUserId, params.get("createUserId"))
                        .isNull(SecUserEntity::getSalt)
        );
        page.getRecords().forEach(i -> {
            Set<Long> roleIdSet = secUserRoleService.list(new LambdaQueryWrapper<SecUserRoleEntity>().eq(SecUserRoleEntity::getUserId, i.getUserId())).stream().map(SecUserRoleEntity::getRoleId).collect(Collectors.toSet());
            i.setSecRoleList(roleIdSet.isEmpty() ? null : secRoleService.listByIds(roleIdSet));
        });
        return new PageUtils(page);
    }

    @Override
    public SecUserEntity getById(Serializable id) {
        Set<Long> roleIdSet = secUserRoleService.list(new LambdaQueryWrapper<SecUserRoleEntity>().eq(SecUserRoleEntity::getUserId, id)).stream().map(SecUserRoleEntity::getRoleId).collect(Collectors.toSet());
        return super.getById(id).setSecRoleList(roleIdSet.isEmpty() ? null : secRoleService.listByIds(roleIdSet));
    }

    @Override
    public SecUserEntity getByUsername(String username) {
        SecUserEntity user = getOne(new LambdaQueryWrapper<SecUserEntity>().eq(SecUserEntity::getUsername, username).isNull(SecUserEntity::getSalt));
        user.setSecRoleList(secRoleService.list(new LambdaQueryWrapper<SecRoleEntity>().in(SecRoleEntity::getRoleId, secUserRoleService.list(new LambdaQueryWrapper<SecUserRoleEntity>().eq(SecUserRoleEntity::getUserId, user.getUserId())).stream().map(SecUserRoleEntity::getRoleId).distinct().collect(Collectors.toList()))));
        return user;
    }

    @Override
    public R getUserBySec() {
        return R.ok().put("user", getByUsername((String) SecurityContextHolder.getContext().getAuthentication().getPrincipal()));
    }

    @Override
    public R getUserByBodyToken(JwtPO po) {

        // TODO MySQL
        // SecUserTokenEntity secUserToken = secUserTokenService.getOne(new LambdaQueryWrapper<SecUserTokenEntity>().eq(SecUserTokenEntity::getToken, po.getJwt()).eq(SecUserTokenEntity::getIdentity, 0));
        // if (secUserToken.getExpireTime().getTime() < System.currentTimeMillis()) {
        //     return R.error(401, "请先登陆...");
        // }
        // return R.ok().put("user", getById(secUserToken.getUserId()));

        // TODO jwt
        return R.ok().put("user", getByUsername(jwtUtils.getClaimByToken(po.getJwt()).getSubject()));

    }

    @Override
    public R register(RegisterPO po) {
        SecUserEntity entity = getByUsername(po.getUsername());
        if (entity != null) {
            return R.error("当前用户名已注册...");
        }
        save(BeanUtil.beanAtoBeanB(po, SecUserEntity.class).setPassword(new BCryptPasswordEncoder().encode(po.getPassword())));
        return R.ok("注册成功...");
    }

    @Override
    public R getUserByHeaderToken(HttpServletRequest request) {

        String jwt = request.getHeader(jwtUtils.getHeader());

        // TODO MySQL
        // SecUserTokenEntity secUserToken = secUserTokenService.getOne(new LambdaQueryWrapper<SecUserTokenEntity>().eq(SecUserTokenEntity::getToken, jwt).eq(SecUserTokenEntity::getIdentity, 0));
        // if (secUserToken.getExpireTime().getTime() < System.currentTimeMillis()) {
        //     return R.error(401, "请先登陆...");
        // }
        // return R.ok().put("user", getById(secUserToken.getUserId()));

        // TODO jwt
        return R.ok().put("user", getByUsername(jwtUtils.getClaimByToken(jwt).getSubject()));
    }

    @Override
    public R getUserBySession(HttpServletRequest request) {
        log.info("getUserBySession sessionId: " + request.getSession().getId());
        return R.ok().put("user", getById((Long) (request.getSession().getAttribute("secUserId"))));
    }

    @Override
    public String getUserAuthorityBySecUserId(Long secUserId) {
        String redisAuth = redisUtils.get(String.format(Constant.SEC_AUTH_KEY, secUserId));

        // TODO 缓存中存过
        if (redisAuth != null){
            return redisAuth;
        }

        StringBuilder userAuthority = new StringBuilder();
        // TODO 获取 角色 idList
        List<Long> secRoleIdList = secUserRoleService.list(new LambdaQueryWrapper<SecUserRoleEntity>().eq(SecUserRoleEntity::getUserId, secUserId)).stream().map(SecUserRoleEntity::getRoleId).distinct().collect(Collectors.toList());
        if (secRoleIdList.size() > 0) {
            // TODO 角色名 str
            String secRoleNameStr = secRoleService.listByIds(secRoleIdList).stream().map(secRole -> "ROLE_" + secRole.getRoleName()).distinct().collect(Collectors.joining(","));
            userAuthority.append(secRoleNameStr);

            // TODO 获取 菜单权限 idList
            List<Long> secMenuIdList = secRoleMenuService.list(new LambdaQueryWrapper<SecRoleMenuEntity>().in(SecRoleMenuEntity::getRoleId, secRoleIdList)).stream().map(SecRoleMenuEntity::getMenuId).distinct().collect(Collectors.toList());
            if (secMenuIdList.size() > 0) {
                // TODO 获取 菜单权限 str
                String secMenuPermStr = secMenuService.listByIds(secMenuIdList).stream().map(SecMenuEntity::getPerms).collect(Collectors.joining(","));
                userAuthority.append(",").append(secMenuPermStr);
            }
        }

        // TODO 放入缓存
        redisUtils.set(String.format(Constant.SEC_AUTH_KEY, secUserId), userAuthority.toString());

        return userAuthority.toString();
    }

    @Override
    public void clearUserAuthorityBySecUserId(Long secUserId) {
        redisUtils.delete(String.format(Constant.SEC_AUTH_KEY, secUserId));
    }

    @Override
    public void clearUserAuthorityBySecRoleIdList(List<Long> secRoleIdList) {
        List<Long> secUserIdList = secUserRoleService.list(new LambdaQueryWrapper<SecUserRoleEntity>().in(SecUserRoleEntity::getRoleId, secRoleIdList)).stream().map(SecUserRoleEntity::getUserId).distinct().collect(Collectors.toList());
        secUserIdList.stream().forEach(this::clearUserAuthorityBySecUserId);
    }

    @Override
    public void clearUserAuthorityBySecMenuIdList(List<Long> secMenuIdList) {
        List<Long> secRoleIdList = secRoleMenuService.list(new LambdaQueryWrapper<SecRoleMenuEntity>().in(SecRoleMenuEntity::getMenuId, secMenuIdList)).stream().map(SecRoleMenuEntity::getRoleId).distinct().collect(Collectors.toList());
        clearUserAuthorityBySecRoleIdList(secRoleIdList);
    }

    @Override
    public Long getUserId() {
        return getByUsername((String) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserId();
    }
}